module nade.physics.collider;

import derelict.newton.newton;
import derelict.opengl.gl;
import derelict.opengl.glu;

import nade.vector;
import nade.primitive;

import nade.physics.world;


class Collider : WorldObject {
	this(World world, NewtonCollision* collision)
	{
		super(world);
		_phys = collision;
	}
	~this()
	{
		NewtonReleaseCollision(world.phys, _phys);
	}

	abstract override void render();
	abstract void calculateInertia(double mass, out double ixx, out double iyy, out double izz);

	NewtonCollision* phys() { return _phys; }

	private {
		NewtonCollision*	_phys;
	}


	static class Box : Collider {
		this(World world, const Vector3 size, GLuint texture = 0)
		{
			_size = size;
			_texture = texture;

			auto collision = NewtonCreateBox(world.phys, _size.x, _size.y, _size.z, null);
			super(world, collision);
		}

		override void render()
		{
			glEnable(GL_TEXTURE_2D);
			glBindTexture(GL_TEXTURE_2D, _texture);
			renderSolidBox(-0.5*_size, 0.5*_size);
			glDisable(GL_TEXTURE_2D);
		}

		override void calculateInertia(double mass, out double ixx, out double iyy, out double izz)
		{
			ixx = mass*(_size.y*_size.y + _size.z*_size.z)/12;
			iyy = mass*(_size.x*_size.x + _size.z*_size.z)/12;
			izz = mass*(_size.x*_size.x + _size.y*_size.y)/12;
		}

		private {
			Vector3		_size;
			GLuint		_texture;
		}
	}


	static class Sphere : Collider {
		this(World world, const Vector3 radius)
		{
			_radius = radius;

			auto collision = NewtonCreateSphere(world.phys, _radius.x, _radius.y, _radius.z, null);
			super(world, collision);
		}

		override void render()
		{
			glScalef(_radius.x, _radius.y, _radius.z);
			auto q = gluNewQuadric();
			gluSphere(q, 1.0f, 20, 20);
			gluDeleteQuadric(q);
		}

		override void calculateInertia(double mass, out double ixx, out double iyy, out double izz)
		{
			ixx = mass*(4*_radius.y*_radius.y + 4*_radius.z*_radius.z)/12;
			iyy = mass*(4*_radius.x*_radius.x + 4*_radius.z*_radius.z)/12;
			izz = mass*(4*_radius.x*_radius.x + 4*_radius.y*_radius.y)/12;
		}

		Vector3 radius() { return _radius; }

		private {
			Vector3		_radius;
		}
	}


	static class Cylinder : Collider {
		this(World world, real radius, real height)
		{
			_radius = radius;
			_height = height;

			auto collision = NewtonCreateCylinder(world.phys, _radius, _height, null);
			super(world, collision);
		}

		override void render()
		{
			glRotatef(90, 0, 1, 0);
			glTranslatef(0, 0, -0.5*_height);
			auto q = gluNewQuadric();
			gluCylinder(q, _radius, _radius, _height, 20, 2);
			gluQuadricOrientation(q, GLU_INSIDE);
			gluDisk(q, 0, _radius, 20, 1);
			glTranslatef(0, 0, _height);
			gluQuadricOrientation(q, GLU_OUTSIDE);
			gluDisk(q, 0, _radius, 20, 1);
			gluDeleteQuadric(q);
		}

		override void calculateInertia(double mass, out double ixx, out double iyy, out double izz)
		{
			ixx = mass*(4*_radius*_radius + 4*_radius*_radius)/12;
			iyy = mass*(_height*_height + 4*_radius*_radius)/12;
			izz = iyy;
		}

		private {
			real		_radius;
			real		_height;
		}
	}


	static class ChamferCylinder : Collider {
		this(World world, real radius, real height)
		{
			_radius = radius;
			_height = height;

			auto collision = NewtonCreateChamferCylinder(world.phys, _radius, _height, null);
			super(world, collision);
		}

		override void render()
		{
			glRotatef(90, 0, 1, 0);
			glTranslatef(0, 0, -0.5*_height);
			auto q = gluNewQuadric();
			gluCylinder(q, _radius, _radius, _height, 20, 2);
			gluQuadricOrientation(q, GLU_INSIDE);
			gluDisk(q, 0, _radius, 20, 1);
			glTranslatef(0, 0, _height);
			gluQuadricOrientation(q, GLU_OUTSIDE);
			gluDisk(q, 0, _radius, 20, 1);
			gluDeleteQuadric(q);
		}

		override void calculateInertia(double mass, out double ixx, out double iyy, out double izz)
		{
			ixx = mass*(4*_radius*_radius + 4*_radius*_radius)/12;
			iyy = mass*(_height*_height + 4*_radius*_radius)/12;
			izz = iyy;
		}

		private {
			real		_radius;
			real		_height;
		}
	}
}
